/**
 * Copyright 2019 吉鼎科技.
 *
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.GetListRequestMessage;
import cn.easyplatform.messages.vos.GetListVo;
import cn.easyplatform.messages.vos.component.ListPageVo;
import cn.easyplatform.messages.vos.datalist.ListDropVo;
import cn.easyplatform.spi.service.ComponentService;
import cn.easyplatform.type.Constants;
import cn.easyplatform.type.FieldVo;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.type.ListRowVo;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.ext.ComponentBuilder;
import cn.easyplatform.web.ext.Node;
import cn.easyplatform.web.ext.zul.BandboxExt;
import cn.easyplatform.web.ext.zul.Datalist;
import cn.easyplatform.web.ext.zul.Loader;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.BackendException;
import cn.easyplatform.web.task.MainTaskSupport;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.support.ExpressionEngine;
import cn.easyplatform.web.task.support.ManagedComponent;
import cn.easyplatform.web.task.support.SupportFactory;
import cn.easyplatform.web.task.support.scripting.CompliableScriptEngine;
import cn.easyplatform.web.task.zkex.ComponentBuilderFactory;
import cn.easyplatform.web.task.zkex.DropSupport;
import cn.easyplatform.web.task.zkex.list.OperableListSupport;
import cn.easyplatform.web.utils.PageUtils;
import cn.easyplatform.web.utils.WebUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.*;
import org.zkoss.zul.event.PagingEvent;
import org.zkoss.zul.event.ZulEvents;

import java.util.*;


/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class LoaderBuilder implements ComponentBuilder, EventListener<Event> {

    //默认列表
    private final static int LIST = 0;
    //具有上下层结构
    private final static int LEVEL = 1;
    //延时加载
    private final static int GROUP = 2;
    //双查询
    private final static int DUAL = 3;
    //分组
    private final static int DEFAULT = 4;

    private Loader loader;

    private OperableHandler main;

    private String draggable;

    private int type = DEFAULT;


    public LoaderBuilder(OperableHandler main, Loader loader) {
        this.main = main;
        this.loader = loader;
    }

    @Override
    public void onEvent(Event evt) throws Exception {
        if (Strings.isBlank(loader.getEntity())) {
            GetListVo gv = new GetListVo(loader.getDbId(), loader.getQuery());
            gv.setFilter(loader.getFilter());
            gv.setPageSize(loader.isFetchAll() ? 0 : loader.getPageSize());
            gv.setOrderBy(loader.getOrderBy());
            gv.setGetCount(gv.getPageSize() > 0);
            GetListRequestMessage req = new GetListRequestMessage(main.getId(),
                    gv);
            ComponentService cs = ServiceLocator
                    .lookup(ComponentService.class);
            IResponseMessage<?> resp = cs.getList(req);
            if (!resp.isSuccess())
                MessageBox.showMessage(resp);
            else {
                if (type == LIST)
                    new ListBuilder().build(resp.getBody());
                else
                    new GroupBuilder().build(resp.getBody());
            }
        } else
            open(evt.getPage());
    }

    @Override
    public Component build() {
        if (Strings.isBlank(loader.getEntity())) {
            if (Strings.isBlank(loader.getQuery()))
                throw new EasyPlatformWithLabelKeyException(
                        "component.property.not.found", "<loader>", "entity");
            if (BandboxExt.DEFAULT.equalsIgnoreCase(loader.getType())) {
                if (Strings.isBlank(loader.getGroup()))
                    type = LIST;
                else
                    type = DEFAULT;
            } else {
                if (Strings.isBlank(loader.getGroup()))
                    throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<loader>", "group");
                if (BandboxExt.DUAL.equalsIgnoreCase(loader.getType())) {
                    if (Strings.isBlank(loader.getGroupQuery()))
                        throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<loader>", "groupQuery");
                    type = DUAL;
                } else if (BandboxExt.GROUP.equalsIgnoreCase(loader.getType())) {
                    if (Strings.isBlank(loader.getGroupQuery()))
                        type = LEVEL;
                    else
                        type = GROUP;
                }
            }
        } else if (Strings.isBlank(loader.getId()))
            loader.setId(RandomStringUtils.randomAlphanumeric(10));
        if (Strings.isBlank(loader.getMapping()))
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<loader>", "mapping");
        if (loader.isMultiple() && loader.isCheckmark()
                && Strings.isBlank(loader.getTarget()))
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<loader>", "target");
        draggable = loader.getDraggable();
        loader.setDraggable("false");
        PageUtils.checkAccess(main.getAccess(), loader);
        loader.addEventListener(Events.ON_CLICK, this);
        if (!Strings.isBlank(loader.getEvent()))
            PageUtils.addEventListener(main, Events.ON_USER, loader);
        return loader;
    }

    private void open(Page page) {
        try {
            Datalist dl = new Datalist();
            dl.setEntity(loader.getEntity());
            dl.setId(loader.getId());
            dl.setType(Constants.LOADER);
            dl.setBefore(loader.getMapping());
            dl.setImmediate(true);
            dl.setPageSize(loader.getPageSize());
            dl.setInvisibleColumns(loader.getInvisibleColumns());
            dl.setSizedByContent(loader.isSizedByContent());
            dl.setShowPanel(true);
            dl.setShowRowNumbers(loader.isShowRowNumbers());
            dl.setLevelBy(loader.getLevelBy());
            dl.setOrderBy(loader.getOrderBy());
            dl.setGroup(loader.getGroup());
            dl.setShowGroupCount(loader.isShowGroupCount());
            dl.setCondition(loader.getQuery());
            dl.setShowPanel(loader.isShowPanel());
            dl.setFrozenColumns(loader.getFrozenColumns());
            dl.setExport(loader.isExport());
            dl.setCheckmark(loader.isCheckmark());
            dl.setMultiple(loader.isMultiple());
            dl.setOddRowSclass(loader.getOddRowSclass());
            dl.setSpan(loader.isSpan());
            dl.setSizable(loader.isSizable());
            dl.setPagingDetailed(loader.isPagingDetailed());
            dl.setPagingAutohide(loader.isPagingAutohide());
            dl.setPagingMold(loader.getPagingMold());
            dl.setPagingStyle(loader.getPagingStyle());
            dl.setCheckall(loader.isCheckall());
            dl.setRowStyle(loader.getRowStyle());
            dl.setHeadStyle(loader.getHeadStyle());
            dl.setAuxHeadStyle(loader.getAuxHeadStyle());
            dl.setFootStyle(loader.getFootStyle());
            dl.setFrozenStyle(loader.getFrozenStyle());
            dl.setInit(loader.getInit());
            dl.setFilter(loader.getFilter());
            dl.setDraggable(draggable);
            dl.setCache(loader.isCache());
            dl.setDepth(loader.getDepth());
            dl.setFetchAll(loader.isFetchAll());
            dl.setWidth(loader.getPanelWidth());
            dl.setHeight(loader.getPanelHeight());
            ComponentBuilder builder = ComponentBuilderFactory
                    .getEntityBuilder(main, dl, loader, null);
            Window win = (Window) builder.build();
            win.setPosition(loader.getPosition());
            win.setTitle(loader.getTitle() == null ? loader.getLabel() : loader.getTitle());
            win.setPage(page);
            win.doOverlapped();
        } catch (EasyPlatformWithLabelKeyException ex) {
            MessageBox.showMessage(
                    Labels.getLabel("message.dialog.title.error"),
                    Labels.getLabel(ex.getMessage(), ex.getArgs()));
        } catch (BackendException ex) {
            MessageBox.showMessage(ex.getMsg().getCode(), (String) ex.getMsg()
                    .getBody());
        }
    }

    ///////////////////////////////下列针对不同的类型使用不同的类来处理/////////////////////////////////

    private interface Builder {
        void build(Object data);
    }

    private abstract class AbstractBuilder implements Builder, EventListener<Event> {

        private Window dialog;

        private List<FieldVo[]> targetData;

        private CompliableScriptEngine engine;

        protected South toolbar = new South();

        @Override
        public void build(Object data) {
            if (!Strings.isBlank(loader.getValidator())) {
                for (ManagedComponent target : ((MainTaskSupport) main)
                        .getManagedEntityComponents()) {
                    if (target.getComponent().getId().equals(loader.getTarget())) {
                        if (target instanceof OperableListSupport) {
                            OperableListSupport os = (OperableListSupport) target;
                            targetData = os.getData(false, false);
                            break;
                        }
                    }
                }
                if (targetData != null) {
                    engine = SupportFactory.getCompliableScriptEngine(main);
                    engine.compile(loader.getValidator());
                }
            }
            toolbar.setBorder("none");
            if (loader.isFetchAll() || loader.getPageSize() <= 0) {
                Hlayout div = new Hlayout();
                div.setSclass("text-center mt-1");
                div.setParent(toolbar);
            } else {
                Vlayout hlayout = new Vlayout();
                hlayout.setHflex("1");
                hlayout.setParent(toolbar);
            }
            Component source = createComponent(data);
            dialog = new Window();
            dialog.setTitle(loader.getTitle() == null ? loader.getLabel() : loader.getTitle());
            dialog.setBorder("normal");
            dialog.setClosable(true);
            dialog.setMaximizable(true);
            dialog.setSizable(true);
            dialog.setPosition(loader.getPosition() == null ? "center" : loader.getPosition());
            dialog.setHeight(loader.getPanelHeight() == null ? "500px" : loader.getPanelHeight());
            dialog.setWidth(loader.getPanelWidth() == null ? "650px" : loader.getPanelWidth());
            createBody(dialog, source);
            dialog.setPage(loader.getPage());
            dialog.doOverlapped();
        }

        private void createBody(Component container, Component source) {
            Borderlayout borderlayout = new Borderlayout();
            borderlayout.setParent(container);
            Center center = new Center();
            center.setHflex("1");
            center.setVflex("1");
            center.setBorder("none");
            center.setParent(borderlayout);
            center.appendChild(source);

            Button close = new Button(Labels.getLabel("button.cancel"));
            close.setIconSclass("z-icon-times");
            close.addForward(Events.ON_CLICK, container, Events.ON_CLOSE);
            Button confirm = new Button(Labels.getLabel("button.ok"));
            confirm.setIconSclass("z-icon-check");
            if (loader.isFetchAll() || loader.getPageSize() <= 0) {
                close.setParent(toolbar.getFirstChild());
                confirm.setParent(toolbar.getFirstChild());
            } else {
                Hlayout div = new Hlayout();
                div.setSclass("text-center");
                close.setParent(div);
                confirm.setParent(div);
                div.setParent(toolbar.getFirstChild());
            }
            confirm.addEventListener(Events.ON_CLICK, this);
            toolbar.setParent(borderlayout);
        }

        @Override
        public void onEvent(Event event) throws Exception {
            Component s = event.getTarget();
            if (s instanceof Button) {
                Button btn = (Button) s;
                //确定按钮
                if (btn.getIconSclass().equals("z-icon-check")) {
                    List<FieldVo[]> items = getSelectedItems();
                    if (!items.isEmpty()) {
                        for (ManagedComponent target : ((MainTaskSupport) main)
                                .getManagedEntityComponents()) {
                            if (target.getComponent().getId().equals(loader.getTarget()) && target instanceof DropSupport) {
                                List<ListRowVo> data = new ArrayList<ListRowVo>(items.size());
                                for (FieldVo[] fvs : items)
                                    data.add(new ListRowVo(fvs));
                                DropSupport os = (DropSupport) target;
                                Events.postEvent(new Event(Events.ON_USER, loader,
                                        "onBefore"));
                                os.drop(new ListDropVo(loader.getMapping(), null, data));
                                Events.postEvent(new Event(Events.ON_USER, loader,
                                        "onAfter"));
                                break;
                            }
                        }
                        dialog.detach();
                    }
                } else//close
                    dialog.detach();
            } else if (event instanceof PagingEvent) {
                PagingEvent pe = (PagingEvent) event;
                GetListVo gv = new GetListVo(loader.getDbId(), loader.getQuery());
                gv.setFilter(loader.getFilter());
                gv.setPageSize(loader.getPageSize());
                gv.setOrderBy(loader.getOrderBy());
                gv.setPageNo(pe.getActivePage() + 1);
                GetListRequestMessage req = new GetListRequestMessage(main.getId(), gv);
                ComponentService cs = ServiceLocator.lookup(ComponentService.class);
                IResponseMessage<?> resp = cs.getList(req);
                if (!resp.isSuccess()) {
                    Clients.wrongValue(event.getTarget(), (String) resp.getBody());
                    return;
                }
                redraw((List<FieldVo[]>) resp.getBody());
            } else
                dispatch(event);
            event.stopPropagation();
        }

        protected boolean validate(Component c) {
            if (engine != null) {
                FieldVo[] source = null;
                if (c instanceof Listitem)
                    source = ((Listitem) c).getValue();
                else
                    source = ((Node) ((Treeitem) c).getValue()).getData();
                for (FieldVo[] target : targetData) {
                    Object obj = engine.eval(source, target);
                    if (obj != null && obj instanceof Boolean && (Boolean) obj) {
                        if (loader.isMultiple() && loader.isCheckmark()) {
                            if (c instanceof Listitem)
                                ((Listitem) c).setSelected(true);
                            else
                                ((Treeitem) c).setSelected(true);
                        } else
                            c.setVisible(false);
                        return true;
                    }
                }
            }
            return false;
        }

        protected void dispatch(Event event) {
        }

        protected abstract List<FieldVo[]> getSelectedItems();

        protected abstract void redraw(List<FieldVo[]> data);

        protected abstract Component createComponent(Object data);
    }

    private class ListBuilder extends AbstractBuilder {

        private Listbox listbox;

        private Paging paging;

        @Override
        protected Component createComponent(Object obj) {
            listbox = new Listbox();
            listbox.setHflex("1");
            listbox.setVflex("1");
            listbox.setSpan(loader.isSpan());
            listbox.setMultiple(loader.isMultiple());
            listbox.setCheckmark(loader.isCheckmark());
            listbox.setSizedByContent(loader.isSizedByContent());
            listbox.setAttribute("ref", loader);
            if (loader.isFetchAll()) {
                listbox.setMold("paging");
                paging = listbox.getPagingChild();
            }
            List<FieldVo[]> data = null;
            if (obj instanceof ListPageVo) {
                if (!loader.isFetchAll() && loader.getPageSize() > 0) {
                    paging = new Paging();
                    paging.setHflex("1");
                    paging.addEventListener(ZulEvents.ON_PAGING, this);
                    toolbar.getFirstChild().appendChild(paging);
                }
                ListPageVo lpv = (ListPageVo) obj;
                data = lpv.getData();
                paging.setTotalSize(lpv.getTotalCount());
            } else {
                data = (List<FieldVo[]>) obj;
            }
            if (paging != null) {
                //paging.setAutohide(true);
                paging.setDetailed(loader.isPagingDetailed());
                paging.setPageSize(loader.getPageSize());
                if (!Strings.isBlank(loader.getPagingMold()))
                    paging.setMold(loader.getPagingMold());
                if (!Strings.isBlank(loader.getPagingStyle()))
                    paging.setStyle(loader.getPagingStyle());
            }
            createHeader();
            redraw(data);
            return listbox;
        }

        @Override
        protected List<FieldVo[]> getSelectedItems() {
            List<FieldVo[]> items = new ArrayList<>();
            for (Listitem li : listbox.getSelectedItems()) {
                if (!validate(li))
                    items.add(li.getValue());
            }
            return items;
        }

        /**
         * 重绘树结点
         *
         * @param data
         */
        protected void redraw(List<FieldVo[]> data) {
            listbox.getItems().clear();
            if (!data.isEmpty()) {
                ExpressionEngine expressionEngine = null;
                Map<String, Object> evalMap = null;
                Map<String, Component> managedComponents = null;
                if (!Strings.isBlank(loader.getRowScript())) {
                    String script = loader.getRowScript();
                    if (loader.getRowScript().startsWith("$")) {
                        script = (String) WebUtils.getFieldValue(main, loader.getRowScript().substring(1));
                        loader.setRowScript(script);
                    }
                    if (script != null) {
                        expressionEngine = SupportFactory.getExpressionEngine(main, script);
                        expressionEngine.compile();
                        evalMap = new HashMap<>();
                        managedComponents = new HashMap<>();
                    }
                }
                try {
                    int cols = listbox.getListhead() == null ? 1 : listbox.getListhead().getChildren().size();
                    for (FieldVo[] fvs : data) {
                        if (expressionEngine != null) {
                            evalMap.clear();
                            managedComponents.clear();
                            for (FieldVo fv : fvs)
                                evalMap.put(fv.getName(), fv.getValue());
                        }
                        Listitem li = new Listitem();
                        for (int i = 0; i < cols; i++) {
                            Object value = fvs[i].getValue();
                            Listcell cell = new Listcell(value == null ? "" : value.toString());
                            cell.setParent(li);
                            if (expressionEngine != null)
                                managedComponents.put(fvs[i].getName(), cell);
                        }
                        li.setValue(fvs);
                        li.setDraggable(draggable);
                        validate(li);
                        listbox.appendChild(li);
                        if (expressionEngine != null)
                            expressionEngine.eval(li, evalMap, managedComponents);
                    }
                } finally {
                    if (expressionEngine != null) {
                        expressionEngine.destroy();
                        expressionEngine = null;
                        evalMap = null;
                        managedComponents = null;
                    }
                }
            }
        }

        /**
         * 创建表头
         */
        private void createHeader() {
            if (!Strings.isBlank(loader.getHeaders())) {
                String title = loader.getHeaders();
                if (title.startsWith("$")) {
                    Object val = WebUtils.getFieldValue(main, title.substring(1));
                    if (val == null || val.toString().equals(""))
                        return;
                    title = (String) val;
                }
                Listhead head = new Listhead();
                head.setSizable(loader.isSizable());
                String[] headers = title.split(",");
                for (String h : headers) {
                    Listheader header = new Listheader(h.trim());
                    header.setHflex("1");
                    head.appendChild(header);
                }
                listbox.appendChild(head);
                if (listbox.getFrozen() == null && loader.getFrozenColumns() > 0) {
                    Frozen frozen = new Frozen();
                    if (!Strings.isBlank(loader.getFrozenStyle()))
                        frozen.setStyle(loader.getFrozenStyle());
                    frozen.setColumns(loader.getFrozenColumns());
                    listbox.appendChild(frozen);
                }
            }
        }
    }

    private class GroupBuilder extends AbstractBuilder {

        private Tree tree;

        private Paging paging;

        @Override
        protected Component createComponent(Object obj) {
            tree = new Tree();
            tree.setHflex("1");
            tree.setVflex("1");
            tree.setSpan(loader.isSpan());
            tree.setMultiple(loader.isMultiple());
            tree.setCheckmark(loader.isCheckmark());
            tree.setSizedByContent(loader.isSizedByContent());
            tree.setAttribute("ref", loader);
            if (loader.isFetchAll()) {
                tree.setMold("paging");
                paging = tree.getPagingChild();
            }
            List<FieldVo[]> data = null;
            if (obj instanceof ListPageVo) {
                if (!loader.isFetchAll() && loader.getPageSize() > 0) {
                    paging = new Paging();
                    paging.setHflex("1");
                    paging.addEventListener(ZulEvents.ON_PAGING, this);
                    toolbar.getFirstChild().appendChild(paging);
                }
                ListPageVo lpv = (ListPageVo) obj;
                data = lpv.getData();
                paging.setTotalSize(lpv.getTotalCount());
            } else {
                data = (List<FieldVo[]>) obj;
            }
            if (paging != null) {
                //paging.setAutohide(true);
                paging.setDetailed(loader.isPagingDetailed());
                paging.setPageSize(loader.getPageSize());
                if (!Strings.isBlank(loader.getPagingMold()))
                    paging.setMold(loader.getPagingMold());
                if (!Strings.isBlank(loader.getPagingStyle()))
                    paging.setStyle(loader.getPagingStyle());
            }
            createHeader();
            redraw(data);
            return tree;
        }

        @Override
        protected List<FieldVo[]> getSelectedItems() {
            List<FieldVo[]> items = new ArrayList<>();
            for (Treeitem ti : tree.getSelectedItems()) {
                if (ti.getValue() != null) {
                    Node node = ti.getValue();
                    if (node.getData() != null && !validate(ti))
                        items.add(node.getData());
                }
            }
            return items;
        }

        /**
         * 重绘树结点
         *
         * @param data
         */
        protected void redraw(List<FieldVo[]> data) {
            if (tree.getTreechildren() != null)
                tree.getTreechildren().detach();
            if (!data.isEmpty()) {
                ExpressionEngine expressionEngine = null;
                if (!Strings.isBlank(loader.getRowScript())) {
                    String script = loader.getRowScript();
                    if (loader.getRowScript().startsWith("$")) {
                        script = (String) WebUtils.getFieldValue(main, loader.getRowScript().substring(1));
                        loader.setRowScript(script);
                    }
                    if (script != null) {
                        expressionEngine = SupportFactory.getExpressionEngine(main, script);
                        expressionEngine.compile();
                    }
                }
                try {
                    Node root = null;
                    if (type == DEFAULT)
                        root = createDefaultModel(data);
                    else
                        root = createGroupModel(data);
                    Treechildren tc = new Treechildren();
                    for (Node node : root.getChildren()) {
                        Treeitem ti = new Treeitem();
                        ti.setValue(node);
                        if (node.getData() == null) {//type=DEFAULT
                            ti.setLabel((String) node.getId());
                            ti.setSelectable(false);
                        } else {
                            createRow(expressionEngine, ti, node.getData());
                            if (type != DUAL)
                                ti.setDraggable(draggable);
                            else
                                ti.setSelectable(false);
                        }
                        if (!node.isLeaf()) {
                            ti.appendChild(new Treechildren());
                            if (loader.isLazy()) {
                                if (loader.getDepth() > 0)
                                    createNode(expressionEngine, ti, node.getChildren());
                                else {
                                    ti.addEventListener(Events.ON_OPEN, this);
                                    ti.setOpen(false);
                                }
                            } else {
                                createNode(expressionEngine, ti, node.getChildren());
                            }//else
                        } else if (type == GROUP || loader.getDepth() > 0) {
                            ti.appendChild(new Treechildren());
                            ti.addEventListener(Events.ON_OPEN, this);
                            ti.setOpen(false);
                        }
                        tc.appendChild(ti);
                    }
                    tree.appendChild(tc);
                } finally {
                    if (expressionEngine != null) {
                        expressionEngine.destroy();
                        expressionEngine = null;
                    }
                }
            }
        }

        @Override
        protected void dispatch(Event event) {
            if (event instanceof OpenEvent) {
                OpenEvent oe = (OpenEvent) event;
                if (oe.isOpen()) {
                    ExpressionEngine expressionEngine = null;
                    if (!Strings.isBlank(loader.getRowScript())) {
                        expressionEngine = SupportFactory.getExpressionEngine(main, loader.getRowScript());
                        expressionEngine.compile();
                    }
                    try {
                        Treeitem ti = (Treeitem) oe.getTarget();
                        if (type == GROUP) {
                            loadGroup(expressionEngine, ti);
                        } else {
                            Node parent = ti.getValue();
                            if (parent.getChildren() == null)
                                loadGroup(expressionEngine, ti);
                            else
                                createNode(expressionEngine, ti, parent.getChildren());
                        }
                        ti.removeEventListener(Events.ON_OPEN, this);
                    } finally {
                        if (expressionEngine != null) {
                            expressionEngine.destroy();
                            expressionEngine = null;
                        }
                    }
                }
            }
        }

        /**
         * 创建子结点
         *
         * @param parent
         * @param children
         */
        private void createNode(ExpressionEngine expressionEngine, Treeitem parent, List<Node> children) {
            for (Node node : children) {
                Treeitem ti = new Treeitem();
                if (node.getData() == null) {
                    ti.setLabel((String) node.getId());
                    ti.setSelectable(false);
                } else {
                    createRow(expressionEngine, ti, node.getData());
                    if (type != DUAL)
                        ti.setDraggable(draggable);
                    else
                        ti.setSelectable(false);
                }
                ti.setValue(node);
                if (!node.isLeaf()) {
                    ti.appendChild(new Treechildren());
                    if (loader.isLazy()) {
                        ti.addEventListener(Events.ON_OPEN, this);
                        ti.setOpen(false);
                    } else {
                        createNode(expressionEngine, ti, node.getChildren());
                        if (loader.getDepth() > 0)
                            ti.setOpen(loader.getDepth() >= node.getLevel());
                    }
                } else if (type == DUAL) {
                    ti.addEventListener(Events.ON_OPEN, this);
                    ti.appendChild(new Treechildren());
                    ti.setOpen(false);
                }
                parent.getTreechildren().appendChild(ti);
            }
            if (type == DUAL)
                loadGroup(expressionEngine, parent);
        }

        /**
         * 创建行
         *
         * @param expressionEngine
         * @param ti
         * @param fvs
         */
        private void createRow(ExpressionEngine expressionEngine, Treeitem ti, FieldVo[] fvs) {
            int cols = tree.getTreecols() == null ? 1 : tree.getTreecols().getChildren().size();
            Map<String, Object> data = null;
            Map<String, Component> managedComponents = null;
            if (expressionEngine != null) {
                data = new HashMap<>();
                for (FieldVo fv : fvs)
                    data.put(fv.getName(), fv.getValue());
                managedComponents = new HashMap<>();
            }
            Treerow tr = new Treerow();
            tr.setParent(ti);
            for (int i = 0; i < cols; i++) {
                Object label = fvs[i].getValue();
                Treecell cell = new Treecell(label == null ? "" : label.toString());
                cell.setParent(tr);
                if (managedComponents != null)
                    managedComponents.put(fvs[i].getName(), cell);
            }
            if (expressionEngine != null) {
                expressionEngine.eval(ti, data, managedComponents);
                data = null;
                managedComponents = null;
            }
            validate(ti);
        }

        /**
         * 加载第2组数据
         *
         * @param ti
         */
        protected void loadGroup(ExpressionEngine expressionEngine, Treeitem ti) {
            Node node = ti.getValue();
            if (Strings.isBlank(loader.getGroupQuery())) {
                int pos = loader.getQuery().toLowerCase().indexOf("where");
                if (pos < 0) {
                    ti.getTreechildren().detach();
                    return;
                } else {
                    StringBuilder sb = new StringBuilder();
                    sb.append(loader.getQuery().substring(0, pos)).append(" WHERE ").append(loader.getGroup().split(",")[1]).append("=?");
                    if (!Strings.isBlank(loader.getOrderBy()))
                        sb.append(" ORDER BY ").append(loader.getOrderBy());
                    loader.setGroup(sb.toString());
                    tree.setAttribute("gg", "");
                }
            } else if (!loader.isFetchAll() && type == DUAL) {
                int pos = loader.getQuery().toLowerCase().indexOf("where");
                if (pos > 0) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(loader.getQuery().substring(0, pos)).append(" WHERE ").append(loader.getGroup().split(",")[1]).append("=?");
                    if (!Strings.isBlank(loader.getOrderBy()))
                        sb.append(" ORDER BY ").append(loader.getOrderBy());
                    GetListVo gv = new GetListVo(loader.getDbId(), sb.toString());
                    gv.setParameter(node.getId());
                    ComponentService cs = ServiceLocator.lookup(ComponentService.class);
                    IResponseMessage<?> resp = cs.getList(new GetListRequestMessage(main.getId(), gv));
                    if (!resp.isSuccess())
                        MessageBox.showMessage(resp);
                    else {
                        List<FieldVo[]> data = (List<FieldVo[]>) resp.getBody();
                        if (!data.isEmpty()) {
                            Integer[] gidx = new Integer[2];
                            String[] groupFields = loader.getGroup().split(",");
                            FieldVo[] first = data.get(0);
                            for (int i = 0; i < first.length; i++) {
                                if (first[i].getName().equalsIgnoreCase(groupFields[0]))
                                    gidx[0] = i;
                                if (first[i].getName().equalsIgnoreCase(groupFields[1]))
                                    gidx[1] = i;
                            }
                            for (FieldVo[] fvs : data) {
                                Treeitem child = new Treeitem();
                                child.setValue(new Node(fvs[gidx[0]].getValue(), fvs[gidx[1]].getValue(), fvs));
                                createRow(expressionEngine, child, fvs);
                                child.appendChild(new Treechildren());
                                child.addEventListener(Events.ON_OPEN, this);
                                child.setOpen(false);
                                child.setSelectable(false);
                                ti.getTreechildren().appendChild(child);
                            }
                        }
                    }
                }
            }
            GetListVo gv = new GetListVo(loader.getDbId(), loader.getGroupQuery());
            gv.setParameter(node.getId());
            ComponentService cs = ServiceLocator.lookup(ComponentService.class);
            IResponseMessage<?> resp = cs.getList(new GetListRequestMessage(main.getId(), gv));
            if (!resp.isSuccess())
                MessageBox.showMessage(resp);
            else {
                List<FieldVo[]> data = (List<FieldVo[]>) resp.getBody();
                if (!data.isEmpty()) {
                    int gidx = 0;
                    if (type == GROUP) {
                        FieldVo[] first = data.get(0);
                        for (; gidx < first.length; gidx++) {
                            if (first[gidx].getName().equalsIgnoreCase(loader.getGroup()))
                                break;
                        }
                    }
                    for (FieldVo[] fvs : data) {
                        Treeitem child = new Treeitem();
                        createRow(expressionEngine, child, fvs);
                        if (type == DUAL) {
                            child.setImage("~./images/weather_sun.png");
                            child.setValue(new Node(fvs));
                        } else {
                            child.setValue(new Node(fvs[gidx].getValue(), fvs));
                            child.appendChild(new Treechildren());
                            child.addEventListener(Events.ON_OPEN, this);
                            child.setOpen(false);
                        }
                        child.setParent(ti.getTreechildren());
                        child.setDraggable(draggable);
                    }//for
                } else if (type == GROUP || tree.getAttribute("gg") != null) //if
                    ti.getTreechildren().detach();
            }//else
        }

        /**
         * 根据数据生成分组模型结构
         *
         * @param data
         * @return
         */
        private Node createGroupModel(List<FieldVo[]> data) {
            if (type == GROUP) {
                Integer gidx = null;
                FieldVo[] first = data.get(0);
                for (int i = 0; i < first.length; i++) {
                    if (first[i].getName().equalsIgnoreCase(loader.getGroup())) {
                        gidx = i;
                        break;
                    }
                }
                if (gidx != null) {
                    Node root = new Node(data.size());
                    for (FieldVo[] fvs : data)
                        root.appendChild(new Node(fvs[gidx].getValue(), fvs));
                    return root;
                }
                return new Node(0);
            } else {
                Integer[] gidx = new Integer[2];
                String[] groupFields = loader.getGroup().split(",");
                FieldVo[] first = data.get(0);
                for (int i = 0; i < first.length; i++) {
                    if (first[i].getName().equalsIgnoreCase(groupFields[0]))
                        gidx[0] = i;
                    if (first[i].getName().equalsIgnoreCase(groupFields[1]))
                        gidx[1] = i;
                }
                if (gidx[0] == null || gidx[1] == null)
                    return new Node(0);
                Node root = new Node(1);
                if (loader.isFetchAll()) {
                    Map<Object, Node> map = new LinkedHashMap<>();
                    List<Node> pending = new ArrayList<>();
                    for (FieldVo[] fvs : data) {
                        Node node = new Node(fvs[gidx[0]].getValue(), fvs[gidx[1]].getValue(), fvs);
                        Node parent = map.get(node.getParentId());
                        if (parent != null)
                            parent.appendChild(node);
                        else
                            pending.add(node);
                        map.put(node.getId(), node);
                    }
                    Iterator<Node> itr = pending.iterator();
                    while (itr.hasNext()) {
                        Node node = itr.next();
                        Node parent = map.get(node.getParentId());
                        if (parent != null) {
                            parent.appendChild(node);
                            itr.remove();
                        } else
                            root.appendChild(node);
                    }
                } else {
                    for (FieldVo[] fvs : data)
                        root.appendChild(new Node(fvs[gidx[0]].getValue(), fvs[gidx[1]].getValue(), fvs));
                }
                return root;
            }
        }

        /**
         * 根据数据生成树模型
         *
         * @param data
         * @return
         */
        private Node createDefaultModel(List<FieldVo[]> data) {
            String[] groups = loader.getGroup().split(";");
            Integer[][] gidx = new Integer[groups.length][];
            FieldVo[] first = data.get(0);
            for (int i = 0; i < groups.length; i++) {
                String group = groups[i];
                String[] fields = group.split(",");
                gidx[i] = new Integer[fields.length];
                for (int j = 0; j < fields.length; j++) {
                    for (int k = 0; k < first.length; k++) {
                        if (fields[j].equalsIgnoreCase(first[k].getName())) {
                            gidx[i][j] = k;
                            break;
                        }
                    }
                }
            }
            //检查是否完整
            for (int i = 0; i < gidx.length; i++) {
                for (int j = 0; j < gidx[i].length; j++) {
                    if (gidx[i][j] == null)
                        return new Node(0);
                }
            }
            Node root = new Node(1);
            Map<Object, Node> map = new HashMap<>();
            StringBuilder sb = new StringBuilder();
            StringBuilder tmp = new StringBuilder();
            for (FieldVo[] fvs : data) {
                for (int i = 0; i < gidx[0].length; i++)
                    sb.append(fvs[gidx[0][i]].getValue());
                String parentId = sb.toString();
                Node parent = map.get(parentId);
                if (parent == null) {
                    parent = new Node(parentId);
                    map.put(parent.getId(), parent);
                    root.appendChild(parent);
                }
                for (int i = 1; i < gidx.length; i++) {
                    tmp.setLength(0);
                    for (int j = 0; j < gidx[i].length; j++) {
                        sb.append(fvs[gidx[i][j]].getValue());
                        tmp.append(fvs[gidx[i][j]].getValue());
                    }
                    Node node = map.get(sb.toString());
                    if (node == null) {
                        node = new Node(tmp.toString());
                        map.put(sb.toString(), node);
                        parent.appendChild(node);
                    }
                    parent = node;
                }
                parent.appendChild(new Node(fvs));
                sb.setLength(0);
            }
            return root;
        }

        /**
         * 创建表头
         */
        private void createHeader() {
            if (!Strings.isBlank(loader.getHeaders())) {
                String title = loader.getHeaders();
                if (title.startsWith("$")) {
                    Object val = WebUtils.getFieldValue(main, title.substring(1));
                    if (val == null || val.toString().equals(""))
                        return;
                    title = (String) val;
                }
                Treecols head = new Treecols();
                head.setSizable(loader.isSizable());
                String[] headers = title.split(",");
                for (String h : headers) {
                    Treecol header = new Treecol(h.trim());
                    header.setHflex("1");
                    head.appendChild(header);
                }
                tree.appendChild(head);
                if (tree.getFrozen() == null && loader.getFrozenColumns() > 0) {
                    Frozen frozen = new Frozen();
                    if (!Strings.isBlank(loader.getFrozenStyle()))
                        frozen.setStyle(loader.getFrozenStyle());
                    frozen.setColumns(loader.getFrozenColumns());
                    tree.appendChild(frozen);
                }
            }
        }
    }
}
